#if 1
/* just to get more temporaries possibly triggering problems ...
 * these kinds of functions are not yet optimized...
 * the vector operations should "currently" trigger gc enough, but the
 * more cases where there could be some dangling pointer being tested,
 * the better
 * "currently" because they should be cached at pointer; at least cached
 * for one level of operations
 */
auto do_add(l, r) {
    return l + r;
}
auto do_sub(l, r) {
    return l - r;
}
auto do_mul(l, r) {
    return l * r;
}
auto do_div(l, r) {
    return l / r;
}
auto do_atan2(l, r) {
    return atan2(l, r);
}
auto do_pow(l, r) {
    return pow(l, r);
}
auto do_hypot(l, r) {
    return hypot(l, r);
}
#else
#  define do_add(l, r)		((l) + (r))
#  define do_sub(l, r)		((l) - (r))
#  define do_mul(l, r)		((l) * (r))
#  define do_div(l, r)		((l) / (r))
#  define do_atan2(l, r)	atan2(l, r)
#  define do_pow(l, r)		pow(l, r)
#  define do_hypot(l, r)	hypot(l, r)
#endif

auto check(a, b) {
    if (complex_p(a) ^ complex_p(b))
	return false;
    else if (complex_p(b)) {
	if (check(real(a), real(b)))
	    return check(imag(a), imag(b));
	return false;
    }
    if (nan_p(a) ^ nan_p(b))
	return false;
    else if (nan_p(b))
	return true;
    if (!!inf_p(a) ^ !!inf_p(b))
	return false;
    if ((!real_p(a) ^ !real_p(b)))
	return false;
    else if (real_p(a) && (a || b) && signbit(a) != signbit(b))
	return false;
    return a == b;
}

auto eval(op, l, r) {
    switch (op) {
	case '+':	l = do_add(l, r);	break;
	case '-':	l = do_sub(l, r);	break;
	case '*':	l = do_mul(l, r);	break;
	case '/':	l = do_div(l, r);	break;
	case 'atan2':	l = do_atan2(l, r);	break;
	case 'pow':	l = do_pow(l, r);	break;
	case 'hypot':	l = do_hypot(l, r);	break;
	default:	throw -1;
    }
    return l;
}

auto function(tid, op) {
    print(">> %4d: %c\n", tid, op);
#if 0
    auto v[3][3] = {
	{                null,   1,  2.0 },
	{ 0x30000000000000000, 1/4,   5l },
	{               6.0*I, 7*I, 8L*I }
    };
#else
    /* ensure value returned vay eval is always the same mp type,
     * to actually trigger problems, as it will call mpfr_set(a, eval(...))
     */
    auto v[3][3] = {
	{ 0.0L, 1.0L, 2.0L },
	{ 3.0L, 4.0L, 5.0L },
	{ 6.0L, 7.0L, 8.0L }
    };
#endif
    auto a, i, j, k, r[3][3];
    for (k = 0; k < 1024; k++) {
	switch (op) {
	    case '+':		r = do_add(v, v);	break;
	    case '-':		r = do_sub(v, v);	break;
	    case '*':		r = do_mul(v, v);	break;
	    case 'atan2':	r = do_atan2(v, v);	break;
	    case 'pow':		r = do_pow(v, v);	break;
	    case 'hypot':	r = do_hypot(v, v);	break;
	    default:		throw -1;
	}
	for (i = 0; i < 3; i++)
	    for (j = 0; j < 3; j++)
		if (!check(r[i][j], a = eval(op, v[i][j], v[i][j]))) {
		    print("%d [%d,%d] = %#s but (%c %#s %#s) = %#s\n",
			  tid, i, j, r[i][j], op, v[i][j], v[i][j], a);
		    return -1;
		}
    }
    print("<< %4d: %c\n", tid, op);
    return 0;
}

void spawn(tid, op) {
    auto t;

    for (; tid < 1024; tid += 6) {
	t = thread(&function, tid, op);
	join(t);
    }
}

/* only opcodes that don't generate exceptions */
auto i, ops[] = { '+', '-', '*', 'atan2', 'pow', 'hypot' };
for (i = 0; i < 6; i++)
    thread(&spawn, i, ops[i % 6]);
